﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Collections.ObjectModel;
using System.Reflection;

namespace Summerset.Isbndb
{
    /// <summary>
    /// Represents the base class for records contained in Pages from the various collections.
    /// </summary>
    public abstract class ResponseElementBase 
    {

        /// <summary>
        /// The XmlElement that acts as the underlying backing field for this object
        /// </summary>
        protected readonly XmlElement _element;

        /// <summary>
        /// Initializes a new instance of the <see cref="ResponseElementBase"/> class.
        /// </summary>
        /// <param name="elementDataNode">The element data node.</param>
        protected internal ResponseElementBase(XmlElement elementDataNode) 
        {
            // validate we received a proper element name
            if (ExpectedElementName != elementDataNode.Name)
                throw new ArgumentException(string.Format("Element name '{0}' did not match expected name {1}", elementDataNode.Name, ExpectedElementName));            

            this._element = elementDataNode;            
        }

        /// <summary>
        /// Gets the xml element name that's expected by the constructor.  
        /// </summary>
        protected abstract string ExpectedElementName { get; }

        /// <summary>
        /// Builds a collection of ResponseElementChildBase objects specified by the T typeparameter.
        /// If there are no elements returned in the data returned by the API, then this method 
        /// returns a null reference.
        /// </summary>
        /// <typeparam name="T">Any ResponseElementChildBase that can be constructed with the data provided by the elementXPath argument.</typeparam>
        /// <param name="elementXPath">The XPath query to retrieve the XmlElements used as the underlying datasource for T</param>
        /// <returns>A populated collection, or null.</returns>
        protected ReadOnlyCollection<T> BuildChildCollection<T>(string elementXPath) where T : ResponseElementChildBase
        {
            
            var nodes = _element.SelectNodes(elementXPath);

            // eventually returned by this method whether or not it's been populated.
            ReadOnlyCollection<T> returnCollection = null;

            if (nodes != null)
            {
                var collection = new Collection<T>();

                /* We need to use reflection here to get the proper constructor for the collection type.
                 * However, we know that all ResponseElementChildBase object have a public constructor that
                 * takes a single XmlElement.
                 */
                ConstructorInfo ctorInfo = typeof(T).GetConstructor(new Type[] { typeof(XmlElement) });

                foreach (XmlNode node in nodes)
                    collection.Add((T)ctorInfo.Invoke(new Object[] { (XmlElement)node }));

                returnCollection = new ReadOnlyCollection<T>(collection);
            }
        
            return returnCollection;
        }

    }
}
